ShowTable of Contents
Part 2: The Customer and Service Agent web interfaces
The
first part of this series showed how to use a server-side automaton or bot to manage connectivity across a customer service desk solution. This second part explains how the agent and customer web interfaces can be implemented using the Sametime Proxy SDK functions.
A digression – Security
A very important aspect of any web application nowadays is to minimize the risks associated with creating an online presence. This article deliberately does not include security functions in the code, in order to better illustrate the concepts here. However, it is something that a developer cannot ignore, which is why this section lists some of the aspects that must be addressed.
While the Sametime Proxy server sanitizes its inputs, any other input fields should be escaped by some mechanism like the Apache Commons Lang library. In particular this applies to the data supplied from the customer web page since this is the externally-facing code.
Listing 1: Sanitizing input
import org.apache.commons.lang3.StringEscapeUtils.escapeHtml3;
String newInput = escapeHtml3(inputData);
Another aspect of security is the concept of authentication. Since we allow anonymous users to connect, we have to allow anonymous users in the Sametime configuration. It is also important to understand that you can never be certain a user is who she says she is if she is not authenticated. To minimize the impact of this, we can restrict an anonymous user's ability to initiate chats in the Sametime Proxy's configuration file
Listing 2: Restricting anonymous users in stproxyconfig.xml
<clientRestriction>
<chat>
<init>true</init>
</chat>
</clientRestriction>
Of course, a better mechanism to handle this situation is to ensure that users log in before accessing the chat function, if this is at all possible.
Note that the code samples accompanying this article refer to JavaScript innerHTML for the sake of simplicity. However, there is a possibility that this can facilitate cross-side scripting (XSS) attacks, so it is safer to construct the DOM fragment correctly instead.
The client side – the agent
This side of the system is the Customer Service Desk application. In our sample, it merely shows the Sametime interactions whereas in a production environment it would have all the other usual customer service functions.
The UI is quite simple. Initially the window displays the usual username and password for the agent to login which, in this case, is achieved by means of the loginByPassword() api.
Figure 1: The initial Agent screen.
When the agent has successfully logged in, we create a livename model for the bot. This allows us to check if the bot is actually active. If it is, a chat channel is opened with the bot and we set up a couple of listeners on this chat, one to catch any incoming message, and one to react if the bot logs out:
Listing 3: Initializing the agent-bot communication
botLNModel = stproxy.getLiveNameModel("servicebot", {"forceWatchlist":true});
// Get (or create) a chat model for the support bot
var chatArgs = {
"isAnonymous" : false,
"isRichText" : false,
"isEmbeded" : true
};
// If the bot is logged in and ready
if (botLNModel.status == stproxy.awareness.AVAILABLE) {
// Establish a chat channel
botChatModel = stproxy.getChatModel(botLNModel.id, chatArgs);
// If the bot sends a message
evtBotMessage = dojo.connect(botChatModel,"onMessage",
dojo.hitch(this, processUserData));
// If the bot logs out
evtBotCLosed = dojo.connect(botChatModel,"onChatClosed",
dojo.hitch(this,function() { agentLogout(true); }));
}
At this point, we're waiting for the bot to let us know that a customer is waiting for attention, so the UI displays an empty form.
Figure 2: The agent screen awaiting input from the Bot.
Now, when a customer requests support of the bot, it sends a chat message with the customer's details to the agent. This information is sent in JSON format, so we have to parse the parts of the message before using them. Fortunately, we can use the browser functions to achieve this, and the fields are then used to populate the screen fields as well as to create a livename – note that the livename's display name is explicitly set here:
Listing 4: Establishing the connection to the customer
function processUserData(json) {
// Parse the JSON data
var inpData = window.JSON.parse(json);
// Set the screen fields to the various values
dojo.byId("emailnode").innerHTML = inpData.email;
dojo.byId("phonenode").innerHTML = inpData.phone;
dojo.byId("reasonnode").innerHTML = inpData.reason;
// Create a livename for the customer
custLiveName = new sametime.LiveName({ "userId":inpData.id,
"isAnonymous":true,
"displayName":inpData.name },
dojo.byId("lnnode"));
// Create a chat channel with the customer
custChatModel = stproxy.getChatModel(inpData.id, { "isAnonymous" : false,
"isEmbedded" : false,
"isRichText" : true });
// Attach a listener for the event marking the end of the chat
evtCustClosed = dojo.connect(custChatModel, "onChatClosed",
dojo.hitch(this, cleanupCustChat));
// Change the agent's status to in a meeting
stproxy.status.set(stproxy.awareness.IN_MEETING, "Chatting with a customer",
statusSet, generalError);
}
At this point, the screen fields are populated, and the agent can chat with the customer via the livename.
Figure 3: The populated agend display with an active LiveName
While this displays the usual Sametime functions, typically you would override the various menu items in order to optimise the customer experience.
The client side – the customer experience
In order to exclude any overhead of loading the Sametime markup when its not needed, the chat function is hosted in a separate popup page. The customer clicks on the “Click here” message, and the popup shows, allowing the customer to specify the relevant details.

Figure 4: The customer page.
The configuration on the page illustrates how certain Sametime functions can be disabled. This is done by disabling some plugins:
Listing 5: Disabling Sametime functions
plugins: {
"cmpFile": false,
"cmpTools": false,
"cmpHelp": false,
"lnmpBizCard":false
}
Here, the
chat
menu
plugins for the File, Tools and Help menus are disabled, along with the
live
name
menu
plugin for the business card: these have the prefix of cmp and lnmp respectively.
Once the customer has input the information and clicks “OK”, it logs the customer in as an anonymous user. In order to initiate the session with the agent, the customer tells the bot via a call to its REST API:
Listing 6: Talking to the bot
dojo.xhrPost({ //
url: "http://coalfish.mul.ie.ibm.com:8080/CustomerServiceDesk/ServiceServlet",
content: {
id: response.id,
user: dojo.byId("nametxt").value,
phone: dojo.byId("phntxt").value,
email: dojo.byId("emailtxt").value,
reason: dojo.byId("reason").value
},
timeout: 5000, // Time in milliseconds
load: function(response, ioArgs) {
dojo.style(dojo.byId("userForm"), {display:'none'});
dojo.style(dojo.byId("chatWindow"), {display:'block'});
dojo.byId("message").innerHTML = response;
return response;
},
error: function(response, ioArgs) {
console.error("HTTP status code: ", ioArgs.xhr.status);
dojo.byId("message").innerHTML = 'Could not connect to Sametime';
return response;
}
});
The code listens for any chat messages to arrive and then processes the chat:
Listing 7: Capturing chat messages
evtHandle = dojo.connect(stproxy.chat, "onChatData",
dojo.hitch(this, createChatWindow));
The code executed when the chat message arrives first creates a chat model for this chat. This grants the code more control of the chat interactions, and facilitates the creation of an embedded chat.
Listing 8: Starting the chat
function createChatWindow(isRichtext, userId) {
var chatArgs = {
"isAnonymous" : false,
"isRichText" : isRichtext,
"isEmbeded" : true
};
// Get the chat model to allow us create a chat
theModel = stproxy.getChatModel(userId, chatArgs);
// Create an embedded chat
theChat = new sametime.Chat({ model: theModel },
document.createElement("div"));
dojo.byId("myChat").appendChild(theChat.domNode);
dojo.empty("message");
// We don't want to redo this, so remove the event
dojo.disconnect(evtHandle);
// Terminate the connection when the agent closes
evtHandle = dojo.connect(theModel, "onChatClosed",
dojo.hitch(this,closeMe));
}
The chat window then displays embedded in the window, and the two partners chat as usual.
When the agent closes the chat window, we capture this event and call the closeMe() function, which terminates the connection to Sametime.
Listing 9: Terminating the Sametime session
function closeMe() {
if (stproxy.isLoggedIn) {
if (theChat)
theChat.destroy();
if (theModel)
theModel.close();
stproxy.login.logout();
alert("Have a nice day!");
}
self.close();
}
You'll notice that it terminates with self.close() - in this case, self refers to the chatModel to which this function is connected. Closing the chatModel in this way ensures that the connection can't be reopened or otherwise misused.
The sample code
The attached code should be dropped into the code from the first article of this series since they together provide the sample application. In the code, specifically in agent.jsp and popup.jsp, there are references to servers that need to be updated. To make it easier, these are marked with the string
which should be replaced by the URL of your Sametime Proxy server including the port, if required.
The attached code uses the Dojo Toolkit, but in fact, this is not required. If you wish to make the solution framework-agnostic, follow the guidlines in the link in the references below.
Conclusion
This second part of the series shows how the customer initiates the request for a chat, but the chat itself is initiated by the agent side. The next and final part of the series will show show to add some more advanced functions to the application, such as inviting a supervisor into the chat, i.e. promoting the chat to n-way.
Creating a customer service web application using Sametime - Part One
Creating a customer service web application using Sametime - Part Three
Resources
Sametime Standard V8.5.2 SDK IFR 1 Multiplatform: https://ibm.biz/BdxaNm
developerWorks® IBM Sametime product page: https://ibm.biz/BdxVZU
Integrating IBM Sametime in a Web application without the Dojo Toolkit: https://ibm.biz/BdxSSJ
About the authors
Brendan Murra
y
Brendan joined Lotus Development in 1991 and was acquired, along with the rest of the company, by IBM in 1995. Most recently he has been working on Sametime with a particular emphasis on its Web functionality. You can reach him at brendan_murray@ie.ibm.com
William Holmes
William has been with IBM since 2007 and is currently the Technical Lead for the Sametime Proxy Web client. You can reach him at HOLMESW@ie.ibm.com